Skip to content

feat(spec-090): admin audit log view#4

Merged
hendrikebbers merged 6 commits intomainfrom
feat/090-audit-log-view
Apr 26, 2026
Merged

feat(spec-090): admin audit log view#4
hendrikebbers merged 6 commits intomainfrom
feat/090-audit-log-view

Conversation

@herbie-bot
Copy link
Copy Markdown

Summary

Adds an /admin/audit-logs admin page (IT-ADMIN only) that lists every INSERT/UPDATE/DELETE event recorded by the platform's audit infrastructure. Entries are shown in a paginated, filterable table sorted by createdAt descending. There is no detail view, no editing, no row-level actions.

Spec

  • Spec folder: specs/090-audit-log-view/
  • Design: specs/090-audit-log-view/design.md
  • Behaviors: specs/090-audit-log-view/behaviors.md
  • Steps: specs/090-audit-log-view/steps.md

Changes

Backend

  • AuditLogController exposes GET /api/audit-logs (paginated, optional entityType / user filters, fixed createdAt DESC sort) and GET /api/audit-logs/entity-types (distinct values for the filter dropdown). Both endpoints are gated by class-level @RequiresItAdmin.
  • CrmAuditLogRepository is a project-local Spring Data repository over AuditLogEntity (from com.open-elements:spring-services). It exists because the dependency's AuditLogDataService exposes only unpaginated List-returning filter methods, which would force in-memory pagination over an unbounded audit table. The fix preserves the design's contract (DB-layer pagination) without modifying the dependency.

Frontend

  • New server page app/(app)/admin/audit-logs/page.tsx — checks ROLE_IT_ADMIN, falls back to <ForbiddenPage />.
  • New client component audit-logs-client.tsx — paginated table (Type / Entity ID / Action / User / Date), two filter dropdowns, page-size selector (10/20/50/100/200, persisted in localStorage["pageSize.auditLogs"], default 20), loading skeleton, empty state, error state. The user dropdown is populated from getUsers({ size: 200 }) and excludes the synthetic System user.
  • Sidebar entry under the existing Admin sub-menu (FileText icon).
  • New API helpers getAuditLogs and getAuditLogEntityTypes in frontend/src/lib/api.ts; new AuditLogDto / AuditAction types in frontend/src/lib/types.ts.
  • nav.auditLogs + full auditLog namespace added to both de.ts and en.ts.

Documentation

  • project-features.md and project-structure.md updated. Two pre-existing UTF-8 corruption marks (U+FFFD) in project-structure.md were repaired in the same edit.

Deviation from design.md

The design referenced auditLogDataService.findByEntityType(entityType, pageable) etc. The actual AuditLogDataService API only provides unpaginated List-returning filter methods. The implementation introduces a small project-local repository to keep filtering at the DB layer; the endpoint contract, query parameters, and response payload are unchanged. The full reasoning is recorded at the top of specs/090-audit-log-view/steps.md.

Test coverage

  • 36 behavioural scenarios in behaviors.md mapped to tests in the coverage table at the top of steps.md.
  • Backend: 9 controller tests (AuditLogControllerTest), 8 role-integration tests (SecurityRoleIntegrationTest), 1 annotation test (PreAuthorizeAnnotationTest). Full backend suite green: 58/58.
  • Frontend: 19 Vitest component tests for AuditLogsClient (loading / empty / error / table rendering / System-user row / pagination defaults / localStorage persistence / page-size resets / dropdown population). All pass.
  • Filter-interaction scenarios that depend on Radix <Select> popover content are not asserted via DOM events — same jsdom limitation accepted in spec 089's review. The filter logic itself is exercised by the backend filter tests for every parameter combination.

Test plan

  • cd backend && ./mvnw verify
  • cd frontend && pnpm test src/app/\(app\)/admin/audit-logs/__tests__/audit-logs-client.test.tsx
  • Manually browse /admin/audit-logs as an IT-ADMIN user and verify table content + filter behaviour against a populated audit log.
  • Verify the page returns a ForbiddenPage when accessed without IT-ADMIN.
  • Verify the sidebar entry is hidden for non-IT-ADMIN users.

Closes #3

🤖 Generated with Claude Code

hendrikebbers and others added 6 commits April 26, 2026 19:11
Link spec 090 to GitHub issue #3 and update status.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Includes a deviation note documenting why a project-local
CrmAuditLogRepository is needed: AuditLogDataService in spring-services
exposes only unpaginated filter methods, which would force in-memory
pagination over the entire audit table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds GET /api/audit-logs (paginated, optional entityType/user filters,
sorted by createdAt DESC) and GET /api/audit-logs/entity-types
(distinct entity-type values for the filter dropdown).

Filtering uses a project-local CrmAuditLogRepository because
AuditLogDataService in spring-services exposes only unpaginated
filter methods (List<AuditLogDto>), which would force in-memory
pagination over an unbounded audit table.

Both endpoints are guarded by class-level @RequiresItAdmin. Adds
9 controller behaviour tests, 8 SecurityRoleIntegrationTest cases,
and a PreAuthorizeAnnotationTest assertion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds /admin/audit-logs admin page (IT-ADMIN only) with:

- Paginated table (Type, Entity ID, Action, User, Date) sorted by
  createdAt DESC
- Two filter dropdowns: entity type (from
  GET /api/audit-logs/entity-types) and user (from existing
  GET /api/users, with the System user filtered out)
- Page-size selector (10/20/50/100/200), persisted in localStorage
  under pageSize.auditLogs, default 20
- Loading / empty / error states; total-count display
- Sidebar nav entry under the Admin sub-menu (FileText icon)

Backend access control is inherited from the controller's class-level
@RequiresItAdmin; the page also gates the route with ROLE_IT_ADMIN
in the server component (ForbiddenPage fallback).

Includes 19 Vitest component tests covering loading/empty/error
states, table rendering with the System user, pagination,
localStorage persistence, and dropdown population.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extend "Admin Pages" feature description with the Users and
  Audit Log sub-pages, IT-ADMIN gating, and the audit log filter
  semantics (System entries only visible without a user filter).
- Add backend auditlog/ package and admin/users/ + admin/audit-logs/
  frontend directories to the project structure tree.
- Fix two pre-existing UTF-8 corruption marks (U+FFFD) in the
  admin/token and admin/brevo lines while editing the section.
- Mark Step 9 of steps.md as complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hendrikebbers hendrikebbers merged commit 98c0a5e into main Apr 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit log view: paginated read-only audit log table

2 participants